package warmachineroster.service.validation;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.util.EContentAdapter;

import warmachineroster.Roster;
import warmachineroster.RosterElement;
import warmachineroster.UnitRosterElement;
import warmachineroster.WarcasterRosterElement;
import warmachineroster.service.validation.calculators.PointValueCalculator;
import warmachineroster.service.validation.calculators.RosterTierCalculator;
import warmachineroster.service.validation.calculators.StructureCalculator;
import warmachineroster.service.validation.calculators.TierStatus;


/**
 * questa classe si occupa di gestire in toto al validazione del singolo roster
 * é u nobserver del roster e ad ogni aggiunta/rimozione di unità ricalcola il pointcost
 * verifica la struttura del roster e ricalcola il tierLevel e i benefici da esso offerti
 * @author paolo
 *
 */
public class RosterValidator extends EContentAdapter {

	private Roster roster;

	private List<IValidationChangeListener> listeners = new ArrayList<IValidationChangeListener>();
	
	private UnitCounter unitCounter = new UnitCounter();
	
	private RosterValidationStatus status;
	
	private RosterTierCalculator tierCalculator;

	private PointValueCalculator pointCalculator;

	private StructureCalculator structureCalculator;
	
	public RosterValidator(Roster roster) {
		this.roster = roster;
		tierCalculator = new RosterTierCalculator(roster, unitCounter);
		pointCalculator = new PointValueCalculator(roster);
		structureCalculator = new StructureCalculator();
		hookModel(roster);
	}

	protected Roster getRoster() {
		return roster;
	}
	
	protected void init() {
		// init the unit count map
		for (RosterElement re : getRoster().getAllRosterElements()) {
			unitCounter.incrementUnitCount(re.getUnitType());
		}
		updateClients();
		
	}

	protected void hookModel(Roster roster) {
		roster.eAdapters().add(this);
	}

	protected void unhookModel() {
		getRoster().eAdapters().remove(this);
	}

	protected void dispose() {
		unhookModel();
	}

	@Override
	public void notifyChanged(Notification notification) {
		int eventType = notification.getEventType();
		switch (eventType) {
		case Notification.ADD:
			Object newValue = notification.getNewValue();
			if (newValue instanceof RosterElement)
				handleAddedElement((RosterElement) newValue);
			else
				throw new IllegalArgumentException(
						"si è cercato di aggiungere un non roster element");
			break;
		case Notification.ADD_MANY:
			// non utilizzata per ora
		case Notification.REMOVE:
			Object oldValue = notification.getOldValue();
			if (oldValue instanceof RosterElement)
				handleRemovedElement((RosterElement) oldValue);
			else
				throw new IllegalArgumentException(
						"si è cercato di rimuovere un non roster element");
			break;
		case Notification.REMOVE_MANY:
			// non utilizzata per ora
		case Notification.SET:
			updateClients();
			break;
		default:
			break;
		}

		super.notifyChanged(notification);
	}

	protected void updateClients() {
		validate();
		for (IValidationChangeListener listener : listeners){
			listener.validatedModel(status);
		}
		
	}

	private void handleRemovedElement(RosterElement removedElement) {
		
		unitCounter.decrementUnitCount(removedElement.getUnitType());
		if (removedElement instanceof WarcasterRosterElement){
			for (RosterElement re : ((WarcasterRosterElement)removedElement).getBattlegroup()){
				unitCounter.decrementUnitCount(re.getUnitType());
			}
		} else if (removedElement instanceof UnitRosterElement){
			for (RosterElement re : ((UnitRosterElement)removedElement).getUnitAttachment()){
				unitCounter.decrementUnitCount(re.getUnitType());
			}
		}
		updateClients();
	}

	private void handleAddedElement(RosterElement addedElement) {
		unitCounter.incrementUnitCount(addedElement.getUnitType());
		updateClients();
	}

	protected void validate() {
		status = new RosterValidationStatus(getRoster().getMaxPointValue());
		TierStatus tierStatus = tierCalculator.calculateTier();
		status.setTierStatus(tierStatus);
		status.setTotalPointCost(pointCalculator.calculatePointValue(tierStatus, unitCounter));
		status.setValidStructure(structureCalculator.calculateStructuralValidity(unitCounter, tierStatus));
	}

	

	protected void addListener(IValidationChangeListener listener) {
		listeners.add(listener);
		listener.validatedModel(status);
	}

	protected void removeListener(IValidationChangeListener listener) {
		listeners.remove(listener);
	}


	

}
